#!/usr/bin/python2.4
#
# Copyright 2008 Google Inc.
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Root page."""

import datetime
import logging
import os
import re
import urllib
import gdata.base
import gdata.base.service
import gdata.service
import gdata.urlfetch
# Use urlfetch instead of httplib
gdata.service.http_request_handler = gdata.urlfetch
import instantvidsite
from google.appengine.api import urlfetch
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
import wsgiref.handlers

logging.getLogger().setLevel(logging.DEBUG)

#constants
youtube_key = [SETUP-youtube_dev_key]
base_query = [SETUP-youtube_default_base_query]
site_tag = [SETUP-youtube_base_dev_tag]


def if_else(condition, t_val, f_val):
  
  """convenience method emulating ternary operator."""
  
  if condition:
    return t_val
  else:
    return f_val


def ProcessVideoEntry(site, entry, from_site=True):

  """convenience method to convert video gdata entry to CachedVideo entity."""
  
  vid = instantvidsite.CachedVideo()
  vid.from_site = from_site
  vid.site = site.key()
  m = re.search('videos/([^<]*)', str(entry.id))
  if not m:
    return None
  vid.id = m.group(1)
  for el in entry.extension_elements:
    if el.tag == 'group':
      for child in el.children:
        if child.tag == 'title':
          vid.title = child.text
        if child.tag == 'description':
          vid.desc = child.text
        if child.tag == 'keywords':
          vid.keywords = child.text
  return vid


def GetTubeUser(site, user=None):
  
  """convenience method to lookup TubeUser."""
  
  if not user:
    user = users.GetCurrentUser()
    # check for existing token
  results = instantvidsite.TubeUser.gql("WHERE user = :1 AND site = :2", user, site.key())
    
  for current in results:
    return current
    
  return None
          
          
def GetAuthSubToken(user=None):
  
  """convenience method to lookup authsub token for current user."""
  
  if not user:
    user = users.GetCurrentUser()
  # check for existing token, from any site profile
  results = instantvidsite.TubeUser.gql("WHERE user = :1", user)
  
  for current in results:  
    if current.auth_token:
      return current.auth_token
        
  # TODO(gabe): check to see if token has been revoked
  return None

def ClearAuthSubToken(user=None):
  
  """convenience method to clear all authsub tokens for current user."""
  
  if not user:
    user = users.GetCurrentUser()
  # check for existing token, from any site profile
  results = instantvidsite.TubeUser.gql("WHERE user = :1", user)
  
  for current in results:  
    if current.auth_token:
      current.auth_token = None
      current.put()
        
  # TODO(gabe): check to see if token has been revoked
  return None
  
  
def BuildAuthHeader(session_token):

  """convert auth token to gdata http header format."""
  
  return 'AuthSub token="' + session_token +'"'


def ConvertVideosToDisplayObjects(vids):
  
  """convert video entities to dicts for passing to template."""
  
  videos = []
  for current in vids:
    vid = {'id': current.id, 'state': current.status,
           'reason': current.reason, 'isLive': current.status == 'live'}
    videos.append(vid)
  return videos

   
def PrintHeader(request, response, site=None, title=None, query=""):
  
  """render the header html for the whole site."""
  
  #TODO - check for error messages, find string value, add to template values
  user = users.GetCurrentUser()
  username = ""
  loggedin = user is not None
  if loggedin:
    username = user.nickname()
  if not title and site:
    title = site.display_name
    
  errormsg = None
  if request.get('error') and request.get('error') != '':
    errormsg = instantvidsite.errormsgs[request.get('error')]
    if not errormsg:
      errormsg = 'Unknown'
      
  template_values = {'logouturl': users.CreateLogoutURL(request.uri),
                     'loginurl': users.CreateLoginURL(request.uri),
                     'username': username,
                     'loggedin': loggedin,
                     'page_title': title,
                     'issiteowner': user == site.owner,                     
                     'site': site,
                     'query': query,
                     'errormsg': errormsg}

  path = os.path.join(os.path.dirname(__file__), 'header.html')
  response.out.write(template.render(path, template_values))


class MainPage(webapp.RequestHandler):
  
  """render the landing page for the site."""
  
  def get(self, sitepath):
    
    """get request."""
    
    user = users.GetCurrentUser()
    loggedin = user is not None
        
    domain_live_videos = []
    backfill_videos = []
    updated_videos = []
    
    #find domain
    site = instantvidsite.GetSite(sitepath)
    if not site:
      logging.error('bad sitepath - '+str(sitepath))
      self.redirect("/?error=siteunknown")
      return
                
    #parse query params
    backfill_params = {}
    
    backfill_params['vq'] = self.request.get('query')
    backfill_params['vq'] = if_else(backfill_params['vq'] is None
                                    or backfill_params['vq'] == "",
                                    site.base_query, site.base_query + ' ' +
                                    backfill_params['vq'])
    backfill_params['orderby'] = self.request.get('orderby')
    backfill_params['orderby'] = if_else(backfill_params['orderby'] is None
                                         or backfill_params['orderby'] == "",
                                         'relevance',
                                         backfill_params['orderby'])
    backfill_params['max-results'] = str(15)
    page = self.request.get('page')
    #logging.debug('page requested:'+page+':')
    page = if_else((page is None) or (page == ""), '1', page)
    backfill_params['start-index'] = str(1+(15*(int(page)-1)))
    
    #config sitevid query params
    sitevid_params = backfill_params.copy()
    sitevid_params['vq'] = self.request.get('query')
    sitevid_params['vq'] = if_else(sitevid_params['vq'] is None
                                    or sitevid_params['vq'] == "",
                                    None, sitevid_params['vq'])
    if sitevid_params['vq'] == None:
      del sitevid_params['vq']
      
    #prep my gdata service    
    service = gdata.service.GDataService()
    service.server = 'gdata.youtube.com'
    
    try:
      #get videos uploaded with this developer tag
      #TODO(gabe): fix developer tag to be truncated db key
      feed = service.Get('/feeds/api/videos/-/%7Bhttp%3A%2F%2Fgdata.youtube.com%2Fschemas%2F2007%2Fdevelopertags.cat%7D'
                         + str(site.path) + '?' + urllib.urlencode(sitevid_params),
                         {'X-GData-Key': ('key='+youtube_key)})
      for entry in feed.entry:
        domainvid = ProcessVideoEntry(site, entry)
        if domainvid:
          domain_live_videos.append(domainvid)
    
      # get some backfill videos    
      feed = service.Get('/feeds/api/videos?'+urllib.urlencode(backfill_params))
      for entry in feed.entry:
        backfillvid = ProcessVideoEntry(site, entry, False)
        if backfillvid:
          backfill_videos.append(backfillvid)
          
      #if i'm still alive, consider storing this information  if previous cache is stale (>1day)
      cached_vids = instantvidsite.CachedVideo.gql("WHERE site = :1 LIMIT 1",site.key())
      #use a very old cache stamp as a default
      stamp = datetime.datetime(2001, 1, 1)
      for cachevid in cached_vids:
        stamp = cachevid.date
        #logging.debug('cache from: '+str(stamp))
        break
      if (datetime.datetime.now() - stamp).days > 1:
        logging.info('updating cache')
        #i'm pretty sure there a parallel programming bug here - 
        #if two requests try to clean the cache at once, something bad will happen
        #that said, transactions are not working like i would expect
        #query = CachedVideo.all()
        #if query.count() > 0:
        # db.delete(query) #can i do this? - no error thrown
        oldarr = []
        oldall = instantvidsite.CachedVideo.gql("WHERE site = :1",site.key())
        for old in oldall:
#            logging.debug(str(old))
          oldarr.append(old)
        for vid in oldarr:
          vid.delete()
        #arr = []
        #arr.extend(domain_live_videos)
        #arr.extend(backfill_videos)
        #db.put(arr)
        for vid in domain_live_videos:
          vid.put()
        for vid in backfill_videos:
          vid.put()
        
#          if len(oldarr) > 0:
#            db.run_in_transaction(swapCache, oldarr, arr)
#          else:
#            db.put(arr)
           
    except urlfetch.DownloadError:
      logging.error('url fetch failed - serving from cache')
      domain_live_videos = instantvidsite.CachedVideo.gql("WHERE from_site = :1 AND site = :2", True, site.key())
      backfill_videos = instantvidsite.CachedVideo.gql("WHERE from_site = :1 AND site = :2", False, site.key())
    except:
      logging.error('url fetch failed - serving from cache')
      domain_live_videos = instantvidsite.CachedVideo.gql("WHERE from_site = :1 AND site = :2", True, site.key())
      backfill_videos = instantvidsite.CachedVideo.gql("WHERE from_site = :1 AND site = :2", False, site.key())
    #retrieve featured vid if this is !query !page
    vid = None
    if site.featured_video and (int(page) == 1) and not self.request.get('query'):
      service = gdata.service.GDataService()
      service.server = 'gdata.youtube.com'      
      try:
        # get video metadata   
        entry = service.Get('/feeds/api/videos/'+site.featured_video)
        vid = ProcessVideoEntry(site, entry)
      except urlfetch.DownloadError:
        pass
      except gdata.service.RequestError:
        pass
      except:
        pass
       
    PrintHeader(self.request, self.response, site, site.display_name, self.request.get('query'))
    
    template_values = {'domain_videos': domain_live_videos,
                       'backfill_videos': backfill_videos,
                       'loggedin': loggedin,
                       'updatedvids': updated_videos,                       
                       'site': site,
                       'vid': vid,
                       'previousurl': if_else(int(page) > 1,
                                              "?page=" +
                                              str(int(page) - 1) +
                                              ";query=" +
                                              self.request.get('query'), None),
                       'nexturl': "?page="+str(int(page) + 1) +
                       ";query=" + self.request.get('query')}

    path = os.path.join(os.path.dirname(__file__), 'browse.html')
    self.response.out.write(template.render(path, template_values))

    
class PlayVideo(webapp.RequestHandler):
  
  """play video request."""
  
  def get(self, sitepath):
    
    """get method."""
    
    #find domain
    site = instantvidsite.GetSite(sitepath)
    if not site:
      logging.error('bad sitepath - '+str(sitepath))
      self.redirect("/?error=siteunknown")
      return
      
    id = self.request.get('id')
    if id is None or id == '':
      self.redirect('/a/'+site.path)
      return
    justfeatured = (self.request.get('justfeatured') == '1')
    
    service = gdata.service.GDataService()
    service.server = 'gdata.youtube.com'
    vid = None
    try:
      # get video metadata   
      entry = service.Get('/feeds/api/videos/'+id)
      vid = ProcessVideoEntry(site, entry)
    except urlfetch.DownloadError:
      pass
    except gdata.service.RequestError:
      pass
    except:
      pass
    
    PrintHeader(self.request, self.response, site, site.display_name +' - ' + id)
    
    template_values = {'id': id,
                       'vid': vid,
                       'site': site,
                       'issiteowner': users.GetCurrentUser() == site.owner,                       
                       'justfeatured': justfeatured}

    path = os.path.join(os.path.dirname(__file__), 'play.html')
    self.response.out.write(template.render(path, template_values))

    
def main():
  application = webapp.WSGIApplication([(r'/a/([^/]*)/PlayVideo', PlayVideo),
                                        (r'/a/([^/]*).*', MainPage)
                                        ], debug=False)
  
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()
